package com.crpc.core.proxy.handler;

import com.crpc.common.crpcEnum.CrpcExceptionEnums;
import com.crpc.common.crpcEnum.RequestType;
import com.crpc.common.exception.CrpcException;
import com.crpc.core.compress.Compressor;
import com.crpc.core.compress.CompressorFactory;
import com.crpc.core.serialize.Serializer;
import com.crpc.core.serialize.SerializerFactory;
import com.crpc.core.transport.message.CrpcRequest;
import com.crpc.core.transport.message.RequestPayLoad;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import lombok.extern.slf4j.Slf4j;

import java.util.Random;

import static com.crpc.common.constant.MessageFormatConstant.*;

/**
 * <pre>
 *     0    1    2    3    4   5    6    7    8    9    10   11   12   13   14   15   16  17    18   19   20   21   22    23   24   25   26   27   28   29   30
 *     +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
 *     /        magic      /ver / head len/  full length      / qt / ser/com /            requestId                  /       timeStamp                       /
 *     +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
 *     /                                                                                                             /                                       /
 *     /                                  body                                                                       /                                       /
 *     /                                                                                                             /                                       /
 *     +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
 * </pre>
 * <p>
 * <p>
 * <p>
 * <p>
 * <p>
 * <p>
 * 4b magic 魔数值 类似于鉴定标志-> crpc.getBytes()
 * 1b version 版本
 * 2b header length 头部长度
 * 4b full length 总的报文长度
 * 1b requestType
 * 1b serialize
 * 1b compress
 * 8b requestId
 * <p>
 * body
 *
 * @author wzy
 * @version 1.0
 * @description: 基于长度字段的帧解码器
 * @date 2023/9/26 15:03
 */
@Slf4j
public class CrpcRequestDecoder extends LengthFieldBasedFrameDecoder {

    public CrpcRequestDecoder() {
        super(
                //最大帧长 超过会直接丢弃
                MaxFrameLength,
                //长度的字段的偏移量
                MAGIC.length + VERSION_LENGTH + HEADER_FIELD_LENGTH,
                //长度字段的长度
                FULL_FIELD_Length,
                //负载的适配长度
                -(MAGIC.length + VERSION_LENGTH + HEADER_FIELD_LENGTH + FULL_FIELD_Length),
                0);
    }

    @Override
    protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
        Thread.sleep(new Random().nextInt(100));
        Object decode = super.decode(ctx, in);
        if (decode instanceof ByteBuf) {
            ByteBuf byteBuf = (ByteBuf) decode;
            return decodeFrame(byteBuf);
        }
        return super.decode(ctx, in);
    }

    private Object decodeFrame(ByteBuf byteBuf) {
        byte[] magic = new byte[MAGIC.length];
        byteBuf.readBytes(magic);
        //检测魔数是否匹配
        for (int i = 0; i < magic.length; i++) {
            if (magic[i] != MAGIC[i]) {
                throw new CrpcException(CrpcExceptionEnums.REQUEST_RESOLVE_ERROR);
            }
        }
        //解析版本号
        byte version = byteBuf.readByte();
        if (version > VERSION) {
            throw new CrpcException(CrpcExceptionEnums.REQUEST_RESOLVE_ERROR);
        }
        //解析头部长度
        short headLength = byteBuf.readShort();
        //解析总长度
        int fullLength = byteBuf.readInt();
        //请求类型
        byte requestType = byteBuf.readByte();
        //序列化类型
        byte serializeType = byteBuf.readByte();
        //压缩类型
        byte compressType = byteBuf.readByte();
        //请求id
        long requestId = byteBuf.readLong();
        //时间戳
        long timeStamp = byteBuf.readLong();
        // 封装
        CrpcRequest crpcRequest = CrpcRequest.builder()
                .requestId(requestId)
                .requestType(requestType)
                .serializeType(serializeType)
                .compressType(compressType)
                .timeStamp(timeStamp)
                .build();
        // 如果是心跳请求,没有负载,此处需要判断然后直接返回
        if (requestType == RequestType.HEART_BEAT.getId()) {
            return crpcRequest;
        }
        //读取负载 获得字节数组 进行解压缩 payLoad
        int payLoadLength = fullLength - headLength;
        byte[] payLoad = new byte[payLoadLength];
        byteBuf.readBytes(payLoad);
        // 解压缩
        Compressor compressor = CompressorFactory.getCompressor(crpcRequest.getCompressType()).getCompressor();
        payLoad = compressor.decompress(payLoad);
        //反序列化
        Serializer serializer = SerializerFactory.getSerializer(crpcRequest.getSerializeType()).getSerializer();
        RequestPayLoad deserialize = serializer.deserialize(payLoad, RequestPayLoad.class);
        crpcRequest.setRequestPayLoad(deserialize);
        log.info("请求：{}，已经在服务端完成请求的解码", crpcRequest.getRequestId());
        return crpcRequest;

    }
}
